#include "StdAfx.h"
#include "CoordinationTaskGoal.h"


CoordinationTaskGoal::CoordinationTaskGoal(const CoordinationSetup* setup, const CoordinationTaskMonitor* taskMonitor)
: m_sync(setup, taskMonitor)
, m_setup(setup)
, m_taskMonitor(taskMonitor)
, m_timeout(0.0f)
{
}

const char* CoordinationTaskGoal::GetName() const
{
	return "Goal";
}

const char* CoordinationTaskGoal::GetDescription() const
{
	return m_goalpipeName.c_str();
}

bool CoordinationTaskGoal::LoadFromXML(int roleID, const XmlNodeRef& rootNode)
{
	const char* name;
	if (!rootNode->getAttr("name", &name))
	{
		AIWarning("Missing 'name' attribute for 'Goal' tag at line %d...", rootNode->getLine());

		return false;
	}

	int typeId = 0;
	if (rootNode->haveAttr("typeId"))
		rootNode->getAttr("typeId", typeId);

	if (rootNode->haveAttr("timeout"))
		rootNode->getAttr("timeout", m_timeout);

	m_goalpipeName = name;

	m_typeId = typeId;

	if (!m_sync.LoadFromXML(roleID, rootNode))
		return false;

	return true;
}

CoordinationTaskGoal::RunningGoal::~RunningGoal()
{
	ClearListeners();
}

void CoordinationTaskGoal::RunningGoal::ClearListeners()
{
	ActorStates::iterator ait = actors.begin();
	ActorStates::iterator aend = actors.end();

	for ( ; ait != aend; ++ait)
	{
		const CoordinationActor& actor = ait->first;
		actor.UnregisterGoalPipeListener(this, ait->second.pipeID);
		--activeCount;
	}
}

void CoordinationTaskGoal::RunningGoal::OnGoalPipeEvent(IPipeUser* pPipeUser, EGoalPipeEvent event, int goalPipeId )
{
	if (event != ePN_Exiting && event != ePN_Deselected && event != ePN_OwnerRemoved)
		return;

	CoordinationActor participant(pPipeUser->GetSpecialAIObject("self"));

	ActorStates::iterator it = actors.find(participant);
	assert(it != actors.end());

	ActorState& state = it->second;
	state.state = CoordinationWaiting;
	//participant.UnregisterGoalPipeListener(this, goalPipeId);

	--activeCount;
}

ECoordinationState CoordinationTaskGoal::GetActorState(const CoordinationID& coordinationID,
														 const CoordinationActor& actor) const
{
	RunningGoals::const_iterator it = m_running.find(coordinationID);
	if (it != m_running.end())
	{
		const RunningGoal& goalpipe = it->second;

		return goalpipe.state;
	}

	return CoordinationNotRunning;
}

ECoordinationState CoordinationTaskGoal::GetState(const CoordinationID& coordinationID) const
{
	RunningGoals::const_iterator it = m_running.find(coordinationID);
	if (it != m_running.end())
	{
		const RunningGoal& goalpipe = it->second;

		return goalpipe.state;
	}

	return CoordinationNotRunning;
}

bool CoordinationTaskGoal::CanStart(const CoordinationID& coordinationID, const CoordinationActors& assignees) const
{
	return true;
}

void CoordinationTaskGoal::Start(const CoordinationID& coordinationID,
								   const CoordinationActors& assignees)
{
	RunningGoals::const_iterator it = m_running.find(coordinationID);
	if (it != m_running.end())
		return;

	std::pair<RunningGoals::iterator, bool> result = m_running.insert(RunningGoals::value_type(coordinationID, RunningGoal()));
	RunningGoal& running = result.first->second;

	CoordinationActors actors = assignees;

	CoordinationActors::iterator ait = actors.begin();
	CoordinationActors::iterator aend = actors.end();

	for ( ; ait != aend; ++ait)
	{
		ActorState& actorState = stl::map_insert_or_get(running.actors, *ait, ActorState());
		actorState.state = CoordinationRunning;
		actorState.timeout = m_timeout;

		CoordinationActor& actor = *ait;
		int goalPipeID = GetAISystem()->AllocGoalPipeId();
		if(actor.SelectGoalPipe(m_typeId, m_goalpipeName.c_str(), goalPipeID))
		{	
			actorState.pipeID = goalPipeID;
			actor.RegisterGoalPipeListener(&running, goalPipeID);
			running.activeCount += 1;
		}
		//If we couldn't start the pipe for this actor, then just assume he's done
		else
		{
			actorState.pipeID = 0;
			actorState.state = CoordinationWaiting;
		}
	}

	running.state = CoordinationRunning;

	m_sync.Start(coordinationID);
}

void CoordinationTaskGoal::Stop(const CoordinationID& coordinationID)
{
	RunningGoals::iterator it = m_running.find(coordinationID);

	if (it != m_running.end())
	{
		RunningGoal& running = it->second;

		ActorStates::iterator ait = running.actors.begin();
		ActorStates::iterator aend = running.actors.end();

		for ( ; ait != aend; ++ait)
			Stop(coordinationID, ait->first);

		m_running.erase(it);
	}

	m_sync.Stop(coordinationID);
}

void CoordinationTaskGoal::Stop(const CoordinationID& coordinationID, const CoordinationActor& actor)
{

	RunningGoals::iterator it = m_running.find(coordinationID);
	assert(it != m_running.end());

	RunningGoal& running = it->second;
	ActorStates::iterator ait = running.actors.find(actor);
	assert(ait != running.actors.end());

	if (ait != running.actors.end())
	{
		ActorState& actorState = ait->second;

		if (actorState.state == CoordinationRunning)
		{
			const CoordinationActor& activeActor = ait->first;
			activeActor.UnregisterGoalPipeListener(&running, ait->second.pipeID);
			--running.activeCount;
			actorState.state = CoordinationFinished;
		}
	}
}

void CoordinationTaskGoal::Update(float frameTime)
{
	if (!m_running.empty())
	{
		m_sync.Update();

		RunningGoals::iterator rit = m_running.begin();
		RunningGoals::iterator rend = m_running.end();

		for ( ; rit != rend; ++rit)
		{
			const CoordinationID& coordinationID = rit->first;
			RunningGoal& running = rit->second;

			ActorStates::iterator ait = running.actors.begin();
			ActorStates::iterator aend = running.actors.end();

			if(m_timeout > 0.0f)
			{
				for ( ; ait != aend; ++ait)
				{
					ActorState& actorState = ait->second;

					if (actorState.state == CoordinationRunning)
					{
						actorState.timeout -= frameTime;

						if (actorState.timeout <= 0.0f)
						{
							const CoordinationActor& actor = ait->first;

							actor.UnregisterGoalPipeListener(&running, actorState.pipeID);
							actorState.state = CoordinationWaiting;
							--running.activeCount;
						}
					}
				}
			}

			if (running.activeCount < 1)
			{
				if (m_sync.GetState(coordinationID) == CoordinationFinished)
					running.state = CoordinationFinished;
				else
					running.state = CoordinationWaiting;
			}
		}
	}
}

void CoordinationTaskGoal::Serialize(TSerialize ser)
{
	ser.Value("m_running", m_running);
	m_sync.Serialize(ser);
}